home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1997 May / PC Plus Super CD Issue 127 (May 1997).iso / delphi1 / lesson4 / todolist / todo1.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-07-07  |  18.5 KB  |  596 lines

  1. unit Todo1;
  2. (* --------------------------------------------------------------
  3.              PC PLUS SAMPLE TODO LIST MANAGER
  4.                 WRITTEN IN BORLAND'S DELPHI
  5.                  AUTHOR: HUW COLLINGBOURNE
  6.    --------------------------------------------------------------
  7.  
  8.    This is a sample application written for a Delphi tutorial series
  9.    in PC Plus (UK) magazine. You may use it or adapt it for your own
  10.    use. You may not sell it. No claims are made for its elegance,
  11.    reliability or suitability for any purpose whatsoever!
  12.  
  13.    This application illustrates several useful features of Delphi,
  14.    such as:
  15.    - String Lists with associated objects
  16.    - Saving and loading records from disk
  17.    - Using the Calendar object
  18.    - Menus and pop-up menus (right click in ToDo list)
  19.    - Multiple unit projects
  20.    - Standard dialog objects (to save and load files)
  21.    - User-defined dialog boxes
  22.    - ToolTip 'hints' (place mouse pointer over CalendarBtn)
  23.    - Date-Time routines
  24.    - RadioGroup objects
  25.  
  26.    --------------------------------------------------------------
  27.       TO USE THE TODO LIST:
  28.    --------------------------------------------------------------
  29.       *  Enter an item into the text entry box, click a Priority in the radio
  30.          button group, pick a date by clicking the button next to the Date
  31.          Due box. Click Add.
  32.  
  33.       *  You can also delete items or you can change an item's text,
  34.          date or priority and save the changes by clicking Replace.
  35.  
  36.       *  To edit an item, double-click it.
  37.  
  38.       *  For mouse shortcuts, single-click the ToDo box with the
  39.          right mouse button.
  40.  
  41.       *  To view an item's associated date and priority (shown in the
  42.          date entry box and the Priority radio group box), single-click
  43.          the item. The Priority Radio Group and the date box display
  44.          the data associated with that item.
  45.  
  46.       *  To have the items sorted (continuously) use the Sorting radio
  47.          button box or the mouse menu.
  48.  
  49.  
  50. --------------------------------------------------------------
  51.    POSSIBLE FUTURE ADDITIONS TO THE APPLICATION:
  52. --------------------------------------------------------------
  53.  
  54.    There are plenty of other features which you might like
  55.    to add to (or change in) this application such as:
  56.    - i/o checking (if you try to save to an empty disk drive, say)
  57.    - error recovery (if you try to load the wrong file type)
  58.    - an option to display dates and priorities alongside each item (maybe
  59.      in another list box?)
  60.    - improved sorting speed by assigning the ToDolist items to a non-visual
  61.      TStrings object prior to sorting.
  62.    - make the ConfirmFileSave function a Form method: TToDoForm.ConfirmFileSave
  63.    - attach free-form memos to items etc. etc...
  64.    These topics have been discussed during the course of the
  65.    tutorial, so go ahead, give it a go!
  66.  
  67. *)
  68.  
  69.  
  70. interface
  71.  
  72. uses
  73.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  74.   Forms, Dialogs, StdCtrls, ExtCtrls,
  75.   StrUtils, Menus, Calend1, SaveMess ;
  76. type
  77.   sortType = set of (bydate, bypriority);
  78. type
  79.   TToDoForm = class(TForm)
  80.     ToDoList: TListBox;
  81.     InputLine: TEdit;
  82.     AddBtn: TButton;
  83.     RadioGroupPriority: TRadioGroup;
  84.     DateDueEditBox: TEdit;
  85.     MainMenu1: TMainMenu;
  86.     FileMenu: TMenuItem;
  87.     SaveMnu: TMenuItem;
  88.     SaveAsMnu: TMenuItem;
  89.     LoadMnu: TMenuItem;
  90.     ExitMnu: TMenuItem;
  91.     NewMnu: TMenuItem;
  92.     OpenDialog1: TOpenDialog;
  93.     SaveDialog1: TSaveDialog;
  94.     RadioGroupSetSort: TRadioGroup;
  95.     DelBtn: TButton;
  96.     ReplaceBtn: TButton;
  97.     PopupMenu1: TPopupMenu;
  98.     Add1: TMenuItem;
  99.     Delete1: TMenuItem;
  100.     Replace1: TMenuItem;
  101.     SortBy1: TMenuItem;
  102.     Name1: TMenuItem;
  103.     Priority1: TMenuItem;
  104.     Date1: TMenuItem;
  105.     Unsorted1: TMenuItem;
  106.     CalendarBtn: TButton;
  107.     Panel1: TPanel;
  108.     ExitBtn: TButton;
  109.     DueByLabel: TLabel;
  110.     Panel2: TPanel;
  111.     procedure AddBtnClick(Sender: TObject);
  112.     procedure ExitMnuClick(Sender: TObject);
  113.     procedure LoadMnuClick(Sender: TObject);
  114.     procedure SaveAsMnuClick(Sender: TObject);
  115.     procedure SaveMnuClick(Sender: TObject);
  116.     procedure NewMnuClick(Sender: TObject);
  117.     procedure RadioGroupSetSortClick(Sender: TObject);
  118.     procedure RadioGroupPriorityClick(Sender: TObject);
  119.     procedure ToDoListDblClick(Sender: TObject);
  120.     procedure ToDoListClick(Sender: TObject);
  121.     procedure FormCreate(Sender: TObject);
  122.     procedure DelBtnClick(Sender: TObject);
  123.     procedure ReplaceBtnClick(Sender: TObject);
  124.     procedure SortList( Sender: TObject );
  125.     procedure Add1Click(Sender: TObject);
  126.     procedure Delete1Click(Sender: TObject);
  127.     procedure Replace1Click(Sender: TObject);
  128.     procedure Name1Click(Sender: TObject);
  129.     procedure Priority1Click(Sender: TObject);
  130.     procedure Date1Click(Sender: TObject);
  131.     procedure Unsorted1Click(Sender: TObject);
  132.     procedure CalendarBtnClick(Sender: TObject);
  133.     procedure ExitBtnClick(Sender: TObject);
  134.   private
  135.     { If an item is added or deleted - the Changed state becomes true       }
  136.     { It is unchanged (Changed is false) after file loading, saving, File   }
  137.     { New and when the ToDo form is first loaded.                           }
  138.     Changed : boolean;
  139.     function OKToClearToDoList( Sender : TObject ) : boolean;
  140.     procedure SetChanged( Sender : TObject ;  status : boolean );
  141.     function IsChanged( Sender : TObject ) : boolean;
  142.     procedure SaveItemsToFile(Sender: TObject ; FileName : string );
  143.     function  SaveChangesDlg(Sender: TObject ) : Word;
  144.   public
  145.     { Public declarations }
  146.   end;
  147.  
  148. var
  149.   ToDoForm: TToDoForm;
  150.  
  151. type
  152.  
  153. { CLASS: ToDoItem }
  154. { objects of this type provide the associated due-by date and priority for
  155.   the strings with which they are associated in the ToDo list.
  156.   Note: the data access methods aren't really needed in this program
  157.   as it stands. After all, you could just as well write: priority := 1
  158.   rather than PutPriority(1) - However, it's good OOP style to use
  159.   such methods and you'll need to be happy with this type of programming if
  160.   you ever plan to write your own visual components in the commercial
  161.   version of Delphi }
  162.  
  163.  ToDoItem = class(TObject)
  164.   priority : integer;
  165.   datedue : TDateTime;
  166.   { These are the data access methods }
  167.   procedure PutPriority( a_priority : integer );
  168.   function GetPriority : integer;
  169.   procedure PutDateDue( a_date : TDateTime );
  170.   function GetDateDue : TDateTime;
  171. end;
  172.  
  173.  
  174. { RECORD: ToDoRec }
  175. { Records are used for file save/load operations. Although it is possible
  176.   to save Objects to disk, this is a complex procedure. It is very much
  177.   simpler to map the object's internal fields (plus its associated string)
  178.   onto a compatible record and use standard file handling routines for
  179.   saving and loading }
  180. type ToDoRecord = record
  181.   str : string;
  182.   priority : integer;
  183.   datedue : TDateTime;
  184. end;
  185.  
  186.  
  187. implementation
  188. {$R *.DFM}
  189.  
  190. function ConfirmFileSave(FileName : string) : boolean;
  191. { put up a dialog box to confirm that the existing file should be
  192.   overwritten. This function returns True if the Yes button is pressed,
  193.   otherwise it returns false. Note that you can test for the
  194.   buttons used in dialogs with these constants: mrNone,mrOk,mrCancel,
  195.   mrAbort,mrRetry,mrIgnore,mrYes,mrNo,mrAbort,mrRetry,mrIgnore,mrAll }
  196. begin
  197.     if MessageDlg(FileName + ' already exists. Save anyway?',
  198.                         mtConfirmation, mbYesNoCancel, 0)
  199.                         = mrYes then
  200.       ConfirmFileSave := true
  201.     else
  202.       ConfirmFileSave := false;
  203. end;
  204.  
  205. { ----------------- }
  206. { ToDoItem methods  }
  207. { ----------------- }
  208.  
  209. procedure ToDoItem.PutPriority( a_priority : integer );
  210. begin
  211.   priority := a_priority;
  212. end;
  213.  
  214. function ToDoItem.GetPriority : integer;
  215. begin
  216.   GetPriority := priority;
  217. end;
  218.  
  219. procedure ToDoItem.PutDateDue( a_date : TDateTime );
  220. begin
  221.   datedue := a_date;
  222. end;
  223.  
  224. function ToDoItem.GetDateDue : TDateTime;
  225. begin
  226.   GetDateDue := datedue;
  227. end;
  228.  
  229. { ----------------- }
  230. { TToDoForm methods }
  231. { ----------------- }
  232. function TToDoForm.OKToClearToDoList( Sender : TObject ) : boolean;
  233. { Returns a true or false value to indicate whether or not it is OK
  234.   to clear the ToDo list (say when File/New or File/Load has been
  235.   selected. Give the user the chance to save ToDo list if necessary. }
  236. var
  237.   saveNow : Word;      { does user want to save to disk? Yes/No/Cancel }
  238.   Continue : boolean;  { carry on and clear the ToDo list? Yes/No      }
  239. begin
  240.      { mrNo, mrYes and mrCancel are constants returned by dialog boxes }
  241.     saveNow := mrNo;
  242.     Continue := true;
  243.        { if changes have been made to the ToDo list since the last
  244.          file save operation, then prompt to save the ToDo list    }
  245.     if IsChanged(Sender) then
  246.        saveNow := SaveChangesDlg( Sender );
  247.        { if user wants to save changes, do so }
  248.     if (saveNow = mrYes) then SaveMnuClick(Sender);
  249.     if (saveNow = mrCancel) then Continue := false
  250.     else
  251.         { if user said Yes to save the file but then cancelled the
  252.           SaveAs operation (so the Changed state is still true)
  253.           then return False - i.e. don't clear the ToDo list }
  254.     if (saveNow = mrYes) and IsChanged(Sender) then Continue := false;
  255.     OKToClearToDoList := Continue;
  256. end;
  257.  
  258. function  TToDoForm.SaveChangesDlg(Sender: TObject ) : Word;
  259. { show dialog - return a constant, mrYes, mrNo, mrCancel }
  260. begin
  261.   SaveChangesDlg := MessageDlg('Save Changes?',
  262.     mtInformation, [mbYes, mbNo, mbCancel], 0);
  263. end;
  264.  
  265.  
  266. procedure TToDoForm.SetChanged( Sender : TObject ;  status : boolean );
  267. begin
  268.   Changed := status;
  269. end;
  270.  
  271. function TToDoForm.IsChanged( Sender : TObject ) : boolean;
  272. begin
  273.   IsChanged := Changed;
  274. end;
  275.  
  276. procedure TToDoForm.SortList( Sender: TObject );
  277. { A Bubble sort - not particulary efficient but has the benefit that,
  278.   as sorting algorithms go, it's fairly simple to understand (honest!) }
  279. var
  280.   i, j  : integer;
  281.   tdi   : ToDoItem;
  282.   count : integer;
  283. begin
  284.   ToDoList.Sorted := False;
  285.   ToDoList.Hide; { Hide for speed up updating }
  286.   count := ToDoList.Items.Count;
  287.   for i := 1 to count do
  288.   begin
  289.     for j := count-1 downto i do
  290.       Case( RadioGroupSetSort.ItemIndex ) of
  291.        2: { sort by: priority }
  292.        if ToDoItem(ToDoList.Items.Objects[j-1]).GetPriority >
  293.          ToDoItem(ToDoList.Items.Objects[j]).GetPriority then
  294.          ToDoList.Items.Exchange(j-1, j);
  295.        3: { sort by: date }
  296.        if ToDoItem(ToDoList.Items.Objects[j-1]).GetDateDue >
  297.          ToDoItem(ToDoList.Items.Objects[j]).GetDateDue then
  298.          ToDoList.Items.Exchange(j-1, j);
  299.       end; { case }
  300.     end;
  301.     ToDoList.Show;
  302. end;
  303.  
  304.  
  305.  
  306. { ---------------- }
  307. { Main Menu Items  }
  308. { ---------------- }
  309. procedure TToDoForm.ExitMnuClick(Sender: TObject);
  310. begin
  311.   if OKToClearToDoList(Sender) then Close;
  312. end;
  313.  
  314. procedure TToDoForm.LoadMnuClick(Sender: TObject);
  315. { Read set of ToDo records from disk }
  316. var
  317.    ToDoFile : file of ToDoRecord;
  318.    anItem : ToDoRecord;
  319.    tdi : ToDoItem;
  320. begin
  321.  if OKToClearToDoList(Sender) then
  322.  begin
  323.    with OpenDialog1 do
  324.      if Execute then
  325.      begin
  326.          ToDoList.Clear;
  327.          InputLine.Text := '';
  328.          { if the specified file exists it loads it, otherwise
  329.          it displays an error message }
  330.        if FileExists(FileName) Then
  331.        begin
  332.          AssignFile(ToDoFile, FileName); { File selected in dialog }
  333.          Reset(ToDoFile);
  334.          while not eof(ToDoFile) do
  335.          begin
  336.            Read(ToDoFile, anItem);  { Read a ToDoRecord record out of the file }
  337.            tdi := ToDoItem.Create;  { map its data onto a ToDoItem object      }
  338.            tdi.PutPriority(anItem.priority);
  339.            tdi.PutDatedue(anitem.datedue);
  340.            ToDoList.Items.AddObject(anItem.str, tdi);
  341.          end;
  342.          CloseFile(ToDoFile);
  343.          Caption := ExtractFilename(FileName);
  344.          SetChanged(Sender,false);
  345.            { use current sort order on newly loaded list }
  346.          RadioGroupSetSortClick(Sender);
  347.        end
  348.        else
  349.          MessageDlg('Sorry. Can''t load this file. '+ FileName +
  350.                             ' does not exist!',
  351.                          mtInformation, [mbOK], 0);
  352.      end; { if Execute }
  353.    end;
  354. end;
  355.  
  356. procedure TToDoForm.SaveItemsToFile(Sender: TObject; FileName : string );
  357. { Saves ToDoItem objects in the form of ToDoRecords to a file }
  358. var
  359.   F : file of ToDoRecord;
  360.   i : integer;
  361.   tdrec : ToDoRecord;
  362. begin
  363.    AssignFile(F, FileName );
  364.    Rewrite(F);
  365.    For i := 0 To ToDoList.Items.Count-1 do
  366.    begin
  367.          { map ob onto record }
  368.      tdrec.str :=  ToDoList.Items.Strings[i];
  369.      tdrec.priority := ToDoItem(ToDoList.Items.Objects[i]).GetPriority;
  370.      tdrec.datedue := ToDoItem(ToDoList.Items.Objects[i]).GetDatedue;
  371.      Write(F, Tdrec );
  372.    end;
  373.     CloseFile(F);
  374.     SaveMsg.MsgLabel.Caption := '   Saving '+ExtractFileName(FileName)+'   ';
  375.     SaveMsg.ShowModal;
  376. end;
  377.  
  378.  
  379. procedure TToDoForm.SaveAsMnuClick(Sender: TObject);
  380. var
  381.    SaveFile : boolean;
  382. begin
  383.   SaveFile := true;
  384.    with SaveDialog1 do
  385.     if Execute then
  386.     begin
  387.       if FileExists(FileName) then
  388.          SaveFile := ConfirmFileSave(FileName);
  389.       if SaveFile then
  390.       begin
  391.          SaveItemsToFile(Sender, FileName );
  392.          Caption := ExtractFilename(FileName);
  393.          OpenDialog1.Filename := Filename;
  394.          SetChanged( Sender, false );
  395.       end;
  396.     end;
  397. end;
  398.  
  399. procedure TToDoForm.SaveMnuClick(Sender: TObject);
  400. begin
  401. if (OpenDialog1.Filename <> '') and (pos('*',OpenDialog1.Filename) = 0) then
  402.                                      { e.g. the wildcard '*.tdo' }
  403.   begin
  404.     SaveItemsToFile(Sender, OpenDialog1.FileName);
  405.     SetChanged( Sender, false );
  406.   end
  407.   else SaveAsMnuClick(Sender);
  408. end;
  409.  
  410. procedure TToDoForm.NewMnuClick(Sender: TObject);
  411. begin
  412.    if OKToClearToDoList(Sender) then
  413.    begin
  414.       ToDoList.Clear;
  415.       InputLine.Text := '';
  416.       OpenDialog1.Filename := '*.tdo';
  417.       Caption := 'ToDo - [Untitled]';
  418.       SetChanged( Sender, false );
  419.     end;
  420. end;
  421.  
  422. { ------------ }
  423. { Radio Groups }
  424. { ------------ }
  425. procedure TToDoForm.RadioGroupSetSortClick(Sender: TObject);
  426. var
  427.   i : integer;
  428. begin
  429.   Case( RadioGroupSetSort.ItemIndex ) of
  430.      0 : { Unsorted } ToDoList.Sorted := False;
  431.      1 : { Name } ToDoList.Sorted := True;
  432.      2,3 : { Priority or Date }  SortList(Sender);
  433.   end;
  434.   ActiveControl := InputLine;
  435. end;
  436.  
  437. procedure TToDoForm.RadioGroupPriorityClick(Sender: TObject);
  438. { return focus to input line }
  439. begin
  440.   ActiveControl := InputLine;
  441. end;
  442.  
  443.  
  444.  
  445. { ---------------------- }
  446. { ToDo List events       }
  447. { ---------------------- }
  448. procedure TToDoForm.ToDoListClick(Sender: TObject);
  449. { when item in list is single clicked, show its date due and its
  450.   priority in the appropriate edit and radio-button boxes }
  451. begin
  452.   DateDueEditBox.Text :=
  453.        DateToStr(ToDoItem(
  454.                  ToDoList.Items.Objects[ToDoList.ItemIndex]).GetDatedue);
  455.   RadioGroupPriority.ItemIndex :=
  456.        ToDoItem(ToDoList.Items.Objects[ToDoList.ItemIndex]).GetPriority - 1;
  457. end;
  458.  
  459. procedure TToDoForm.ToDoListDblClick(Sender: TObject);
  460. { when double clicked, load the current item's string into the InputLine
  461.   ToDoListClick will execute also }
  462. begin
  463.   InputLine.Text := ToDoList.Items.Strings[ToDoList.ItemIndex];
  464. end;
  465.  
  466. { ---------------------- }
  467. { Form-level event       }
  468. { ---------------------- }
  469. procedure TToDoForm.FormCreate(Sender: TObject);
  470. begin
  471.    DateDueEditBox.Text := DateToStr(Date);
  472.    SetChanged( Sender, False );
  473.    { I've used a panel as a reference area. The form
  474.      resizes around the panel, so it should look more
  475.      or less OK at most screen resolutions }
  476.    ClientHeight := Panel1.Height;
  477.    ClientWidth := Panel1.Width;
  478.    CalendarBtn.Height := dateDueEditBox.Height;
  479.    CalendarBtn.Top := dateDueEditBox.Top;
  480. end;
  481.  
  482. { ---------------------- }
  483. { Button clicks          }
  484. { ---------------------- }
  485. procedure TToDoForm.AddBtnClick(Sender: TObject);
  486. { add new item to ToDoList }
  487. var
  488.    CurrentItem : ToDoItem;
  489.    NewStr : string;
  490. begin
  491.   NewStr := TrimEnds(InputLine.Text);
  492.   If NewStr = '' Then
  493.      MessageDlg('There is no item to add!', mtInformation,
  494.       [mbOk], 0)
  495.   else
  496.   begin
  497.       { if no date is specified, use Today's date }
  498.     if DateDueEditBox.Text = '' then
  499.      DateDueEditBox.Text := DateToStr(Now);
  500.       { create a new ToDoItem object }
  501.      CurrentItem:= ToDoItem.Create;
  502.      With CurrentItem do
  503.       { set its fields using values on form }
  504.      begin
  505.        Putpriority(RadioGroupPriority.ItemIndex+1);
  506.        PutDateDue(StrToDate(DateDueEditBox.Text));
  507.      end;
  508.      ToDoList.Items.AddObject(NewStr, CurrentItem);
  509.      InputLine.Text := '';
  510.      SetChanged( Sender, true );
  511.   end;
  512.   ActiveControl := InputLine;
  513.   RadioGroupSetSortClick(Sender); { re-sort if necessary }
  514. end;
  515.  
  516. procedure TToDoForm.DelBtnClick(Sender: TObject);
  517. begin
  518.   if ToDoList.ItemIndex >= 0 then
  519.   begin
  520.      ToDoList.Items.Delete(ToDoList.ItemIndex);
  521.      SetChanged( Sender,true );
  522.   end
  523.   else
  524.     MessageDlg('You must select an item to delete!', mtInformation,
  525.       [mbOk], 0)
  526. end;
  527.  
  528. procedure TToDoForm.ReplaceBtnClick(Sender: TObject);
  529. begin
  530.   if (TrimEnds(InputLine.Text) = '') then
  531.      MessageDlg('You must enter a new item.', mtInformation,
  532.       [mbOk], 0)
  533.   else if (ToDoList.ItemIndex < 0) then
  534.      MessageDlg('You must select the item you wish to replace.', mtInformation,
  535.       [mbOk], 0)
  536.   else
  537.   begin
  538.      DelBtnClick(Sender);
  539.      AddBtnClick(Sender);
  540.   end
  541. end;
  542.  
  543. { ---------------- }
  544. { Mouse menu items }
  545. { ---------------- }
  546. procedure TToDoForm.Add1Click(Sender: TObject);
  547. begin
  548.   AddBtnClicK(Sender);
  549. end;
  550.  
  551. procedure TToDoForm.Delete1Click(Sender: TObject);
  552. begin
  553.   DelBtnClick(Sender);
  554. end;
  555.  
  556. procedure TToDoForm.Replace1Click(Sender: TObject);
  557. begin
  558.   ReplaceBtnClick(Sender);
  559. end;
  560.  
  561. procedure TToDoForm.Name1Click(Sender: TObject);
  562. begin
  563.   RadioGroupSetSort.ItemIndex := 1;
  564.   RadioGroupSetSortClick(Sender);
  565. end;
  566.  
  567. procedure TToDoForm.Priority1Click(Sender: TObject);
  568. begin
  569.   RadioGroupSetSort.ItemIndex := 2;
  570.   RadioGroupSetSortClick(Sender);
  571. end;
  572.  
  573. procedure TToDoForm.Date1Click(Sender: TObject);
  574. begin
  575.   RadioGroupSetSort.ItemIndex := 3;
  576.   RadioGroupSetSortClick(Sender);
  577. end;
  578.  
  579. procedure TToDoForm.Unsorted1Click(Sender: TObject);
  580. begin
  581.   RadioGroupSetSort.ItemIndex := 0;
  582.   RadioGroupSetSortClick(Sender);
  583. end;
  584.  
  585. procedure TToDoForm.CalendarBtnClick(Sender: TObject);
  586. begin
  587.   CalForm.ShowModal;
  588. end;
  589.  
  590. procedure TToDoForm.ExitBtnClick(Sender: TObject);
  591. begin
  592.   ExitMnuClick(Sender);
  593. end;
  594.  
  595. end.
  596.